﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data.EntityClient;
using System.Data.SqlClient;
using System.Data;

namespace VLUSM_DataAccess.Repositories
{
    public class GenericRepository<TContext> : IGenericRepository, IDisposable where TContext : ObjectContext
    {
        // Cached ObjectSets so changes persist
        protected Dictionary<string, object> CachedObjects = new Dictionary<string, object>();
        protected ObjectSet<TEntity> GetObjectSet<TEntity>() where TEntity : EntityObject
        {
            var fulltypename = typeof(TEntity).AssemblyQualifiedName;
            if (fulltypename == null)
                throw new ArgumentException("Invalid Type passed to GetObjectSet!");
            if (!CachedObjects.ContainsKey(fulltypename))
            {
                var objectset = context.CreateObjectSet<TEntity>();
                CachedObjects.Add(fulltypename, objectset);
            }
            return CachedObjects[fulltypename] as ObjectSet<TEntity>;
        }
        protected TContext context;

        public TContext Context
        {
            get { return context; }
            set { context = value; }
        }

        /// <summary>
        /// Constructor that takes a context
        /// </summary>
        /// <param name="context">An established data context</param>
        public GenericRepository(TContext context)
        {
            this.context = context;
        }

        /// <summary>
        /// Constructor that takes a connection string and an EDMX name
        /// </summary>
        /// <param name="connectionString">The connection string</param>
        /// <param name="edmxName">The name of the EDMX so we can build an Entity Connection string</param>
        public GenericRepository(string connectionString, string edmxName)
        {
            Type contextType = typeof(TContext);

            //SqlConnectionStringBuilder SqlBuild = new SqlConnectionStringBuilder(connectionString);
            //SqlBuild.DataSource = @".\SQLEXPRESS";
            //SqlBuild.InitialCatalog = "VLUSM";
            //SqlBuild.IntegratedSecurity = false;
            //SqlBuild.MultipleActiveResultSets = true;
            //SqlBuild.PersistSecurityInfo = true;
            //SqlBuild.UserID = "sa";
            //SqlBuild.Password = "123";

            //EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder();
            //builder.ProviderConnectionString = SqlBuild.ToString();
            //builder.Provider = "System.Data.SqlClient";
            //builder.Metadata = String.Format("res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl", edmxName);

            object objContext = Activator.CreateInstance(contextType, connectionString);
            this.context = (TContext)objContext;
        }

        public IQueryable<TEntity> Fetch<TEntity>() where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>();
        }

        public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : EntityObject
        {

            return GetObjectSet<TEntity>().AsEnumerable();
        }

        public IEnumerable<TEntity> Find<TEntity>(Func<TEntity, bool> predicate) where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>().Where(predicate);
        }

        public TEntity GetSingle<TEntity>(Func<TEntity, bool> predicate) where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>().Single(predicate);
        }

        public TEntity GetSingleOrDefault<TEntity>(Func<TEntity, bool> predicate) where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>().SingleOrDefault(predicate);
        }

        public TEntity GetFirst<TEntity>(Func<TEntity, bool> predicate) where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>().First(predicate);
        }

        public IEnumerable<TEntity> GetLookup<TEntity>() where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>().ToList();
        }

        public void Add<TEntity>(TEntity entity) where TEntity : EntityObject
        {
            if (entity == null)
                throw new ArgumentException("Cannot add a null entity");
            GetObjectSet<TEntity>().Attach(entity);
            //GetObjectSet<TEntity>().AddObject(entity);
            context.ObjectStateManager.ChangeObjectState(entity, EntityState.Added);
        }

        public void Delete<TEntity>(TEntity entity) where TEntity : EntityObject
        {
            if (entity == null)
                throw new ArgumentException("Cannot delete a null entity");

            GetObjectSet<TEntity>().Attach(entity);
            context.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);
        }

        public void Attach<TEntity>(TEntity entity) where TEntity : EntityObject
        {
            if (entity == null)
                throw new ArgumentException("Cannot attach a null entity");

            GetObjectSet<TEntity>().Attach(entity);
            context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
        }

        public bool Any<TEntity>(Func<TEntity, bool> predicate) where TEntity : EntityObject
        {
            return GetObjectSet<TEntity>().Any(predicate);
        }

        public void SaveChanges()
        {
            SaveChanges(SaveOptions.None);
        }

        public void SaveChanges(SaveOptions options)
        {
            context.SaveChanges(options);
        }

        public void Refresh<TEntity>(TEntity entity) where TEntity : EntityObject
        {
            context.Refresh(RefreshMode.StoreWins, entity);
        }

        public void Refresh<TEntity>(IEnumerable<TEntity> entities) where TEntity : EntityObject
        {
            context.Refresh(RefreshMode.StoreWins, entities);
        }

        #region IDisposable implementation
        /// <summary>
        /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
        /// </summary>
        /// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (Context != null)
                {
                    Context.Dispose();
                    Context = null;
                }
            }
        }
        #endregion
    }
}
